
package com.itcast.spring6.aop.annotation;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * @author zhangtian105
 * @date 2023/5/11
 * @apiNote
 */
@Slf4j
@Aspect
@Component
// 数值越小优先级越高
@Order(value = 1)
public class AspectAnnotationConfig {

    /*
     * 学习使用：
     * 表达式中增加了args(time, name)部分，意味着可以在增强处理的签名方法（access方法）中定义"time"和"name"两个属性。
     * 这两个形参的类型可以随意指定(access方法中指定)，但一旦指定了这两个参数的类型，
     * 则这两个形参类型将用于限制该切入点只匹配第一个参数类型为Date，
     * 第二个参数类型为String的方法（方法参数个数和类型若有不同均不匹配）；
     * access方法只需要满足"time", "name"参数的顺序和pointcut中args(param1, param2)的顺序相同即可，"returnValue"位置顺序无所谓
     */
    /*@AfterReturning(
            pointcut="execution(* com.itcast.spring6.*.access*(..)) && args(time, name)",
            returning="returnValue")
    public void access(Date time, Object returnValue, String name) {
        System.out.println("目标方法中的参数String = " + name);
        System.out.println("目标方法中的参数Date = " + time);
        System.out.println("目标方法的返回结果returnValue = " + returnValue);
    }*/

    /**
     * 通过 &&、 ||、 !、 的方式进行组合
     * 任意公共方法的执行：execution(public * *(..))
     * 任何一个以“set”开始的方法的执行：execution(* set*(..))
     * AccountService 接口的任意方法的执行：execution(* com.xyz.service.AccountService.*(..))
     * 定义在service包里的任意方法的执行： execution(* com.xyz.service.*.*(..))
     * 定义在service包和所有子包里的任意类的任意方法的执行：execution(* com.xyz.service..*.*(..))
     *      第一个*表示匹配任意的方法返回值， ..(两个点)表示零个或多个，第一个..表示service包及其子包,第二个*表示所有类, 第三个*表示所有方法，第二个..表示方法的任意参数个数
     * 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行：execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
     * pointcutexp包里的任意类： within(com.test.spring.aop.pointcutexp.*)
     * pointcutexp包和所有子包里的任意类：within(com.test.spring.aop.pointcutexp..*)
     * 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类：this(com.test.spring.aop.pointcutexp.Intf)
     *      当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型
     * 带有@Transactional标注的所有类的任意方法：
     *      @within(org.springframework.transaction.annotation.Transactional)
     *      @target(org.springframework.transaction.annotation.Transactional)
     * 带有@Transactional标注的任意方法：@annotation(org.springframework.transaction.annotation.Transactional)
     *      @within和@target针对类的注解,@annotation是针对方法的注解
     * 参数带有@Transactional标注的方法：@args(org.springframework.transaction.annotation.Transactional)
     * 参数为String类型(运行是决定)的方法： args(String)
     */
    @Pointcut(value = "execution(public * *(..))")
    private void pointcut() {}

    @Before("pointcut()")
    public void beforeAnyPublicMethod(JoinPoint jointPoint) {
        log.info("@Before spring6.AspectXmlConfig.beforeAnyPublicMethod ......") ;
    }

    @Around("pointcut()")
    public Object aroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("@Around spring6.AspectXmlConfig.aroundPublicMethod ......, {}", joinPoint.getSignature()) ;

        Random random = new Random() ;
        // 抛出异常模拟出异常通知After-Throwing
        /*if (random.nextBoolean()) {
            throw new RuntimeException("For purpose XML configuration") ;
        }*/

        Long start = System.currentTimeMillis() ;
        Object process = joinPoint.proceed() ;

        log.info("执行耗时: {}", System.currentTimeMillis() - start);

        return process ;
    }

    @After("pointcut()")
    public void finalizeAnyPublicMethod() {
        log.info("@After spring6.AspectXmlConfig.finalizeAnyPublicMethod ......") ;
    }

    @AfterReturning(value = "pointcut()",returning = "result")
    public Object afterAnyPublicMethod(JoinPoint joinPoint, Object result) {
        log.info("@After-Return spring6.AspectXmlConfig.afterAnyPublicMethod ...... {}", result) ;
        return result ;
    }

    @AfterThrowing(value = "pointcut()",throwing = "throwable")
    public void afterThrowingAnyPublicMethod(JoinPoint jointPoint,Throwable throwable) {
        log.info("@After-Throwing spring6.AspectXmlConfig.afterThrowingAnyPublicMethod ......") ;
    }
}
